//
// Created by GF on 2023/10/27.
//

#include <stdio.h> /* fopen(); */
#include <string.h> /* strlen(); */
#include <malloc.h> /* malloc(); realloc(); */
#include "Text_Function.h"
#include "Array_Function.h"

/* CSV - 获取Csv总行数 */
int Csv_Get_Rows_Total_Num(char* Csv_Path) {

    FILE *fp;
    int Counter = 0;
    char Row[512];
    /* ********************************************** */
    fp = fopen(Csv_Path, "r");
    /* ********************************************** */
    if (fp == NULL) {
        printf("Csv Notice: File Pointer is Empty, Get Rows Total Number Failed.\n");
    } else {
        /*
         * char *fgets(char *s, int size, FILE *stream):
         * s - 是一个字符数组, 用于存储读取到的字符串.
         * size - 是一个整型数值, 表示最大读取的字符数(包括空格在内的所有字符).
         * stream - 是一个文件指针, 指定要读取的文件流.
         *
         * fgets()函数用于从指定的文件流中读取一行字符串, 并将其存储到指定的字符数组中, 可以指定最大读取的字符数, 以防止缓冲区溢出.
         * 当读取(n-1)个字符时, 或者读取到换行符时, 或者到达文件末尾时, 它会停止, 具体视情况而定.
         * 如果成功读取到字符串, 则返回该字符数组的地址.
         * 如果读取失败(例如遇到文件结束符或发生错误), 则返回NULL.
         */
        while (fgets(Row, 512, fp)) Counter += 1;
    }
    /* ********************************************** */
    fclose(fp);
    /* ********************************************** */
    return Counter;
}

/* CSV - 获取Csv总列数 */
int Csv_Get_Cols_Total_Num(char* Csv_Path) {

    FILE *fp;
    int Ch_ASCII, Counter = 0;
    /* ********************************************** */
    fp = fopen(Csv_Path, "r");
    /* ********************************************** */
    if (fp == NULL) {
        printf("Csv Notice: File Pointer is Empty, Get Cols Total Number Failed.\n");
    } else {
        /* 十进制ASCII码"10"为"LF/NL", 也就是"\n" */
        while ((Ch_ASCII = getc(fp)) != 10) {
            /* 十进制ASCII码中，32为SPACE(空格)，34为"(英文双引号)，39为'(英文单引号), 44为,(英文逗号) */
            if (Ch_ASCII == 44) Counter += 1;
        }
        /*
         * Example:
         * "Field1, Field2, Field3, Field4, Field5" 共有5个字段.
         * 但是以分隔的逗号计数结果为4, 因此需要在计数完后, 再加1.
         */
        Counter = Counter + 1;
    }
    /* ********************************************** */
    fclose(fp);
    /* ********************************************** */
    return Counter;
}

/* CSV - 首行读取为指针数组(PtrArray) */
char** Csv_First_Row_Read_As_PtrArray(char* Csv_Path) {

    FILE *fp;
    int Ch_ASCII;
    char First_Row[1024];
    /* ********************************************** */
    fp = fopen(Csv_Path, "r");
    /* ********************************************** */
    if (fp == NULL) {
        printf("Csv Notice: File Pointer is Empty, First Row Read as PtrArray Failed.\n");
    } else {
        /*
         * 十进制ASCII码"10"为"LF/NL"，也就是"\n"。
         * 十进制ASCII码"44"为","，也就是"英文逗号"。
         */
        while ((Ch_ASCII = getc(fp)) != 10) {
            /*
             * sprintf(char* Dest, const char* Format,...):
             * sprintf函数是C语言中的格式化函数之一，用于将格式化的字符串输出到指定的字符数组中。
             */
            sprintf(First_Row, "%s%c", First_Row, Ch_ASCII);
        }
    }
    /* ********************************************** */
    fclose(fp);
    /* ********************************************** */
    /* Calling Other Function: Char PArray - Read Csv First Row to PArray */
    char** PtrArray = Char_PtrArray_Split_String_To_PtrArray(First_Row, ",");
    /* ********************************************** */
    return PtrArray;
}

/* CSV - 读取为CsvString */
char* Csv_Read_As_CsvString(char* Csv_Path) {

    FILE* fp;
    signed char Ch_ASCII;
    /* ********************************************** */
    /*
     * malloc()分配空间地址后返回的是一个指针.
     * sizeof(char) * 3 : 因为1个汉字占2-3个Char.
     */
    char* CsvString = (char*)malloc(sizeof(char) * 3);
    /* ********************************************** */
    /*
     * CSV文件编码需要与输出终端编码相同, 否则中文输出为乱码;
     * 输出终端为GBK则需要转换成GBK或GB2312编码;
     * 输出终端为UTF-8则需要转换成UTF-8编码.
     */
    fp = fopen(Csv_Path, "r");
    /* ********************************************** */
    if (fp == NULL) {
        printf("Csv Notice: File Pointer is Empty, Read as CsvString Failed.\n");
        /* ****************************************** */
        fclose(fp);
        /* ****************************************** */
        return NULL;
    } else {
        int i = 1;
        while ((Ch_ASCII = (signed char)getc(fp)) != EOF) {
            CsvString[(i - 1)] = Ch_ASCII;
            /* ************************************** */
            //分配一个全新的内存空间。
            CsvString = realloc(CsvString, (sizeof(char) * 3 * i));
            /* ************************************** */
            i++;
        }
        /* ****************************************** */
        fclose(fp);
        /* ****************************************** */
        return CsvString;
    }
}

/* CSV - 读取为二维指针数组(TwoDimPtrArray) */
char*** Csv_Read_As_TwoDimPtrArray(char* Csv_Path) {

    /* Calling Other Function */
    int Max_Rows_Num = Csv_Get_Rows_Total_Num(Csv_Path); /* CSV - 获取Csv总行数 */
    int Max_Cols_Num = Csv_Get_Cols_Total_Num(Csv_Path); /* CSV - 获取Csv总列数 */
    char* CsvString = Csv_Read_As_CsvString(Csv_Path); /* CSV - 读取为CsvString */
    CsvString = Csv_CsvString_Wash_By_Pointer_Mov(CsvString); /* CSV - CsvString清洗 - 指针移动法 */
    char*** TwoDimPtrArray = Char_TwoDimPtrArray_Init(Max_Rows_Num, Max_Cols_Num); /* 二维字符指针数组 - 初始化 (Init) */
    /* ********************************************** */
    int Read_Char_Num = 0;
    char Row_Content[1024] = {0};
    unsigned long long CsvString_Length = strlen(CsvString);
    /* ********************************************** */
    /* Double constraints must be used, otherwise access array may exceed limits */
    for (int i = 0, Row = 0; i < CsvString_Length && Row < Max_Rows_Num; Row++) {
        sscanf((CsvString + i), "%s%n", Row_Content, &Read_Char_Num);
        /* ****************************************** */
        /* Calling Other Function: Char PtrArray - Split String to PtrArray */
        char** Row_Content_PtrArray = Char_PtrArray_Split_String_To_PtrArray(Row_Content, ",");
        /* ****************************************** */
        for (int Col = 0; Col < Max_Cols_Num; Col++) TwoDimPtrArray[Row][Col] = Row_Content_PtrArray[Col];
        /* ****************************************** */
        i = i + Read_Char_Num + 1;
    }
    /* ********************************************** */
    return TwoDimPtrArray;
}

/* CSV - CsvString清洗 - 指针移动法 */
char* Csv_CsvString_Wash_By_Pointer_Mov(char* CsvString) {

    unsigned long long CsvString_Length = strlen(CsvString);
    /* ********************************************** */
    char* New_CsvString = (char*)malloc(sizeof(char) * CsvString_Length);
    /* ********************************************** */
    int i = 0;
    while (*CsvString != '\0') { /* 使用指针访问字符 */
        if ((signed char)*CsvString != 32 && (signed char)*CsvString != 34) {
            New_CsvString[i] = *CsvString;
            i++;
        }
        /* ****************************************** */
        CsvString++;
    }
    /* ********************************************** */
    return New_CsvString;
}

/* CSV - CsvString清洗 - Sscanf过滤法 */
char* Csv_CsvString_Wash_By_Sscanf(char* CsvString) {

    int Read_Char_Num = 0;
    char Buffer[1024] = {0};
    unsigned long long CsvString_Length = strlen(CsvString);
    /* ********************************************** */
    char* New_CsvString = (char*)malloc(sizeof(char) * CsvString_Length);
    /* ********************************************** */
    memset(New_CsvString, 0, sizeof(char) * CsvString_Length);
    /* ********************************************** */
    for (int i = 0; i < CsvString_Length; i = i + Read_Char_Num + 1) {
        /*
         * int scanf(const char *format, ...);
         * int sscanf(const char *str, const char *format, ...);
         *
         * scanf("%c%n", Buffer1, &Buffer2); 其中%c读取1个字符(Char), 其中%n记录读取的字符数量(整数).
         * scanf("%[0-9]%[a-z]%[,.?]", Buffer1, Buffer2, Buffer3); 对多个字符进行匹配. 此例匹配三个变量, 分别是数字, 字母和符号.
         * scanf("%[^\n]", Buffer); 直到某个字符停止读取, 此例读取字符直到遇到\n.
         */
        sscanf((CsvString + i),"%*[^\"]%n", &Read_Char_Num);
        /* ****************************************** */
        if (Read_Char_Num > 1024) {
            printf("Csv Notice: The Number of Char Read is Greater Than The Length of Buffer, CsvString Wash By Sscanf is Failed.\n");
            break;
        }
        /* ****************************************** */
        sscanf((CsvString + i),"%[^\"]%n", Buffer, &Read_Char_Num);
        /* ****************************************** */
        /*
         * strcat(char *Dest, const char *Src):
         * C语言字符串操作函数之一，用于将源字符串追加到目标字符串的末尾。
         * Dest表示目标字符串，Src表示源字符串，返回值为目标字符串的指针。
         */
        New_CsvString = strcat(New_CsvString, Buffer);
    }
    /* ********************************************** */
    return New_CsvString;
}
